use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Expr, ItemFn, Lit, Meta, Token};
use syn::parse::{Parse, ParseStream};
use syn::punctuated::Punctuated;

pub fn do_rpc_path(attr: TokenStream, item: TokenStream) -> TokenStream {
    let input = parse_macro_input!(item as ItemFn);
    let path_info = parse_macro_input!(attr as PathInfo);
    let path = path_info.name;
    let timeout = path_info.timeout;

    let vis = input.vis;
    let sig = input.sig;
    // let block = input.block;

    let expanded = quote! {
        #vis #sig {
            let bytes = rpc::encode(&*self.context, data)?;
            let local_seq = self.context.get_local_seq_creator().create_seq();
            let header = rpc::dto::RpcHeader::new(#path, local_seq.clone(), 0);
            let rpc_data_bytes = rpc::dto::RpcDataBytes::new(header, bytes);
            let result = rpc::encode_rpc_data_bytes(&*self.context, rpc_data_bytes)?;

            let rpc_data_bytes = rpc::dto::RpcDataBytes::new(rpc::dto::RpcHeader::new(#path, local_seq, 0),  result);
            let data = channel.write(rpc_data_bytes, #timeout).await?;
            let value = rpc::decode(&*self.context, data)?;
            Ok(value)
        }
    };
    TokenStream::from(expanded)
}

struct PathInfo {
    name: String,
    timeout: u32,
}

impl Parse for PathInfo {
    fn parse(input: ParseStream) -> syn::Result<Self> {
        let metas = Punctuated::<Meta, Token![,]>::parse_terminated(input)?;

        let mut name: String = String::from("");
        let mut timeout: u32 = 60;
        for meta in metas {
            if let Meta::NameValue(name_value) = meta {
                if name_value.path.is_ident("timeout") {
                    if let Expr::Lit(nv) = name_value.value {
                        if let Lit::Str(lit_str) = &nv.lit {
                            name = lit_str.value();
                        }
                    }
                } else if name_value.path.is_ident("name") {
                    if let Expr::Lit(nv) = name_value.value {
                        if let Lit::Int(lit) = &nv.lit {
                            timeout = lit.base10_parse::<u32>().unwrap();
                        }
                    }
                }
            }
        }
        Ok(PathInfo { name, timeout })
    }
}